// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.bookmarks;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import org.chromium.chrome.R;
import org.chromium.components.bookmarks.BookmarkId;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * BaseAdapter for BookmarkDrawerListView. It manages items to list there.
 */
class BookmarkDrawerListViewAdapter extends BaseAdapter {
    static final int TYPE_FOLDER = 0;
    static final int TYPE_DIVIDER = -1;
    static final int TYPE_FOLDERS_TITLE = -2;

    static final int VIEW_TYPE_ITEM = 0;
    static final int VIEW_TYPE_DIVIDER = 1;
    static final int VIEW_TYPE_TITLE = 2;

    private BookmarkDelegate mDelegate;
    private List<Item> mTopSection = new ArrayList<Item>();
    private List<Item> mBottomSection = new ArrayList<Item>();
    // array containing the order of sections
    private List<?>[] mSections = {mTopSection, mBottomSection};

    private BookmarkId mDesktopNodeId = null;
    private BookmarkId mMobileNodeId = null;
    private BookmarkId mOthersNodeId = null;
    private List<BookmarkId> mManagedAndPartnerFolderIds = null;

    /**
     * Represents each item in the list.
     */
    static class Item {
        final int mType;
        final BookmarkId mFolderId;

        Item(int itemType) {
            mType = itemType;
            mFolderId = null;
        }

        Item(BookmarkId folderId) {
            assert folderId != null;
            mType = TYPE_FOLDER;
            mFolderId = folderId;
        }

        @Override
        public int hashCode() {
            // hash function generated by Eclipse
            final int prime = 31;
            int result = 1;
            result = prime * result + ((mFolderId == null) ? 0 : mFolderId.hashCode());
            result = prime * result + mType;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null) return false;
            if (getClass() != obj.getClass()) return false;
            Item other = (Item) obj;
            if (mFolderId == null) {
                if (other.mFolderId != null) return false;
            } else if (!mFolderId.equals(other.mFolderId)) {
                return false;
            }
            if (mType != other.mType) {
                return false;
            }
            return true;
        }
    }

    private void repopulateTopSection() {
        mTopSection.clear();
        if (mDelegate.getModel().isFolderVisible(mMobileNodeId)) {
            mTopSection.add(new Item(mMobileNodeId));
        }
        if (mDelegate.getModel().isFolderVisible(mDesktopNodeId)) {
            mTopSection.add(new Item(mDesktopNodeId));
        }
        if (mDelegate.getModel().isFolderVisible(mOthersNodeId)) {
            mTopSection.add(new Item(mOthersNodeId));
        }
        if (mManagedAndPartnerFolderIds != null) {
            for (BookmarkId id : mManagedAndPartnerFolderIds) {
                mTopSection.add(new Item(id));
            }
        }
    }

    void updateList() {
        mDesktopNodeId = mDelegate.getModel().getDesktopFolderId();
        mMobileNodeId = mDelegate.getModel().getMobileFolderId();
        mOthersNodeId = mDelegate.getModel().getOtherFolderId();
        mManagedAndPartnerFolderIds = mDelegate.getModel().getTopLevelFolderIDs(true, false);
        repopulateTopSection();

        setTopFolders(mDelegate.getModel().getTopLevelFolderIDs(false, true));
        notifyDataSetChanged();
    }

    /**
     * Sets folders to show.
     */
    void setTopFolders(List<BookmarkId> folders) {
        mBottomSection.clear();

        if (folders.size() > 0) {
            // Add a divider and title to the top of the section.
            mBottomSection.add(new Item(TYPE_DIVIDER));
            mBottomSection.add(new Item(TYPE_FOLDERS_TITLE));
        }

        // Add the rest of the items.
        for (BookmarkId id : folders) {
            mBottomSection.add(new Item(id));
        }
    }

    /**
     * Clear everything so that it doesn't have any entry.
     */
    void clear() {
        mTopSection.clear();
        mBottomSection.clear();
    }

    void setBookmarkUIDelegate(BookmarkDelegate delegate) {
        mDelegate = delegate;
    }

    /**
     * Get the position in the list of a given item
     * @param item Item to search for
     * @return index of the item or -1 if not found
     */
    int positionOfItem(Item item) {
        int offset = 0;
        for (List<?> section : mSections) {
            int index = section.indexOf(item);
            if (index != -1) {
                return offset + index;
            }
            // If not found in current section, offset the potential result by
            // the section size.
            offset += section.size();
        }
        return -1;
    }

    /**
     * Get the position in the list of a given bookmark folder id
     * @param id Id of bookmark folder
     * @return index of bookmark folder or -1 if not found
     */
    int positionOfBookmarkId(BookmarkId id) {
        return positionOfItem(new Item(id));
    }

    /**
     * Get item position of the given mode.
     */
    int getItemPosition(int state, Object modeDetail) {
        if (state == BookmarkUIState.STATE_FOLDER) {
            Set<BookmarkId> topLevelFolderParents = new HashSet<>();
            topLevelFolderParents.addAll(mDelegate.getModel().getTopLevelFolderParentIDs());
            topLevelFolderParents.add(mDesktopNodeId);
            topLevelFolderParents.add(mOthersNodeId);
            topLevelFolderParents.add(mMobileNodeId);

            // Find top folder id that contains |folder|.
            BookmarkId topFolderId = (BookmarkId) modeDetail;
            while (true) {
                BookmarkId parentId =
                        mDelegate.getModel().getBookmarkById(topFolderId).getParentId();
                if (topLevelFolderParents.contains(parentId)) {
                    break;
                }
                topFolderId = parentId;
            }
            return positionOfBookmarkId(topFolderId);
        }

        return -1;
    }

    // BaseAdapter implementations.

    @Override
    public boolean areAllItemsEnabled() {
        return false;
    }

    @Override
    public boolean isEnabled(int position) {
        Item item = (Item) getItem(position);
        return item.mType != TYPE_DIVIDER && item.mType != TYPE_FOLDERS_TITLE;
    }

    @Override
    public int getItemViewType(int position) {
        Item item = (Item) getItem(position);
        if (item.mType == TYPE_DIVIDER) {
            return VIEW_TYPE_DIVIDER;
        } else if (item.mType == TYPE_FOLDERS_TITLE) {
            return VIEW_TYPE_TITLE;
        } else {
            return VIEW_TYPE_ITEM;
        }
    }

    @Override
    public int getViewTypeCount() {
        return 3;
    }

    @Override
    public int getCount() {
        int sum = 0;
        for (List<?> section : mSections) {
            sum += section.size();
        }
        return sum;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public Object getItem(int position) {
        if (position < 0) {
            return null;
        }
        for (List<?> section : mSections) {
            if (position < section.size()) {
                return section.get(position);
            }
            position -= section.size();
        }
        return null;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final Item item = (Item) getItem(position);

        // Inflate view if convertView is null.
        if (convertView == null) {
            final int itemViewType = getItemViewType(position);
            if (itemViewType == VIEW_TYPE_ITEM) {
                convertView = LayoutInflater.from(parent.getContext()).inflate(
                        R.layout.bookmark_drawer_item, parent, false);
            } else if (itemViewType == VIEW_TYPE_DIVIDER) {
                convertView = LayoutInflater.from(parent.getContext()).inflate(
                        R.layout.bookmark_divider, parent, false);
            } else if (itemViewType == VIEW_TYPE_TITLE) {
                convertView = LayoutInflater.from(parent.getContext()).inflate(
                        R.layout.bookmark_drawer_title, parent, false);
            } else {
                assert false : "Invalid item view type.";
            }
        }

        if (item.mType == TYPE_DIVIDER) return convertView;

        if (item.mType == TYPE_FOLDERS_TITLE) {
            String title = convertView.getContext().getResources().getString(
                    R.string.bookmark_drawer_folders);
            ((TextView) convertView).setText(title);
            return convertView;
        }

        final BookmarkDrawerListItemView listItemView =
                (BookmarkDrawerListItemView) convertView;
        String title;
        int iconDrawableId;

        switch (item.mType) {
            case TYPE_FOLDER:
                title = mDelegate.getModel().getBookmarkById(item.mFolderId).getTitle();
                if (mManagedAndPartnerFolderIds != null
                        && mManagedAndPartnerFolderIds.contains(item.mFolderId)) {
                    iconDrawableId = R.drawable.bookmark_managed;
                } else if (item.mFolderId.equals(mMobileNodeId)
                        || item.mFolderId.equals(mOthersNodeId)
                        || item.mFolderId.equals(mDesktopNodeId)) {
                    iconDrawableId = R.drawable.bookmark_folder;
                } else {
                    iconDrawableId = 0;
                }
                break;
            default:
                title = "";
                iconDrawableId = 0;
                assert false;
        }

        listItemView.setText(title);
        listItemView.setIcon(iconDrawableId);

        return convertView;
    }
}
